package traffic_restriction

import (
	"sync"
	"time"
)

type list struct {
	listLock  sync.Mutex
	name      string
	head      *node
	len       int64
	maxLength int64
	waiting   bool
	waitTime  int64
}

type node struct {
	nanoTime int64
	next     *node
}

func (n *node) add(nn *node) {
	if n.next != nil {
		n.next.add(nn)
	} else {
		n.next = nn
	}
}

type MemeryTRHandler struct {
	hLock   sync.RWMutex
	dataSet map[string]*list
}

var memTRHandler = new(MemeryTRHandler)

func init() {
	memTRHandler.dataSet = make(map[string]*list)
	RegistTRHandler("mem", memTRHandler)
}

func (h *MemeryTRHandler) Do(str string, cfg LimitConfig) bool {
	h.hLock.RLock()
	l, ok := h.dataSet[str]
	h.hLock.RUnlock()
	if !ok {
		l = new(list)
		l.name = str
		l.maxLength = cfg.MaxAccessTime
		l.waitTime = cfg.DurationTime
		h.hLock.Lock()
		h.dataSet[str] = l
		h.hLock.Unlock()
	}
	return l.add(time.Duration(l.waitTime))
}

func (l *list) wait() {
	l.listLock.Lock()
	if l.waiting {
		l.listLock.Unlock()
		return
	}
	l.waiting = true
	l.listLock.Unlock()
	for {
		if l.head.next == nil {
			break
		}
		nxt := l.head.nanoTime
		<-time.After(time.Unix(nxt, 0).Sub(time.Now()))
		l.deleteFirst()
	}
	l.listLock.Lock()
	l.waiting = false
	l.listLock.Unlock()
}

func (l *list) first() *node {
	return l.head
}

func (l *list) deleteFirst() {
	l.listLock.Lock()
	l.head = l.head.next
	l.len--
	l.listLock.Unlock()
}

func (l *list) add(d time.Duration) bool {
	if l.len >= l.maxLength {
		return false
	}
	n := time.Now().Add(time.Second * d).Unix()
	l.listLock.Lock()
	newNode := new(node)
	newNode.nanoTime = n
	if l.head == nil {
		l.head = newNode
	} else {
		l.head.add(newNode)
	}
	l.listLock.Unlock()
	l.len++
	go l.wait()
	return true
}
